/*******************************************************************************
 * Copyright (c) 2011 Gijs de Vries aka Janoz.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Gijs de Vries aka Janoz - initial API and implementation
 ******************************************************************************/
/* COPYRIGHT (C) 2008 Gijs de Vries. All Rights Reserved. */
package com.janoz.httpd;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Properties;

/**
 * Response object containing the content to be sent back to the 
 * client by PicoHttpd.
 * 
 * @author Gijs de Vries
 */
public class Response {

	private static final String defaultCharset = "UTF-8";
	private static final String defaultEncoding = "UTF8";
	
	private StatusCode code;
	private Properties headers;
	private byte[] content;

	
	public static Response createEncodingSave(StatusCode code) {
		try {
			return new Response(code);
		} catch (UnsupportedEncodingException uee) {
			return new Response("UTF8 encoding not supported on platform the server is running on..".getBytes(),null,MimeType.TEXT,code,null);
		}
	}
	
	/**
	 * Renders an html page with only the statuscode as content.
	 * @param code A http return code 
	 * @throws UnsupportedEncodingException when default encoding isn't available
	 */
	public Response(StatusCode code) throws UnsupportedEncodingException {
		this("<html><head><title>" 
				+ code 
				+ "</title></head><body><h1>" 
				+ code 
				+ "</h1></body></html>", MimeType.HTML, code, null);
	}
	/**
	 * Returns the content as a html page with OK status.
	 * @param content The html content.
	 * @throws UnsupportedEncodingException when default encoding isn't available
	 */
	public Response(String content) throws UnsupportedEncodingException {
		this(content.getBytes(defaultEncoding), defaultCharset,  MimeType.HTML, StatusCode.OK, null);
	}
	/**
	 * Returns the content with the given mimetype and the OK status. 
	 * @param content the content to be returned.
	 * @param type the mimetype of the content.
	 * @throws UnsupportedEncodingException when default encoding isn't available
	 */
	public Response(String content, MimeType type) throws UnsupportedEncodingException  {
		this(content.getBytes(defaultEncoding), defaultCharset, type, StatusCode.OK, null);
	}
	/**
	 * Returns the content with the given mimetype and the OK status.
	 * @param content the content to be returned.
	 * @param type the mimetype of the content.
	 */
	public Response(byte[] content, MimeType type) {
		this(content, null, type, StatusCode.OK, null);
	}
	/**
	 * Returns the content with the given mimetype and the OK status.
	 * @param content the content to be returned.
	 * @param charset the characterset or null if none used
	 * @param type the mimetype of the content.
	 */
	public Response(byte[] content, String charset, MimeType type) {
		this(content, charset, type, StatusCode.OK, null);
	}
	/**
	 * Returns the content with the given mimetype an statuscode and headers.
	 * @param content the content to be returned.
	 * @param type the mimetype of the content.
	 * @param code the statuscode
	 * @param headers a set of headers
	 * @throws UnsupportedEncodingException when default encoding isn't available
	 */
	public Response(String content, MimeType type, 
			StatusCode code, Properties headers)  throws UnsupportedEncodingException {
		this(content.getBytes(defaultEncoding), defaultCharset, type, code, headers);
	}
	/**
	 * Returns the content with the given mimetype an statuscode and headers.
	 * @param content the content to be returned.
	 * @param charset the characterset or null if none used
	 * @param type the mimetype of the content.
	 * @param code the statuscode
	 * @param headers a set of headers
	 */
	public Response(byte[] content, String charset, MimeType type, 
			StatusCode code, Properties headers) {
		this.headers = new Properties();
		if (headers != null) {
			this.headers.putAll(headers);
		}
		if (charset == null) {
			type.addToHeader(this.headers);
		} else {
			type.addToHeader(this.headers,charset);
		}
		this.headers.setProperty("Content-Length", ""+content.length);
		this.content = content; 
		this.code = code;
	}

	/**
	 * Method to explicitly add servername afterwards. Used by the httpd class
	 * @param name the servername
	 */
	public void setServerName(String name) {
		headers.setProperty("Server", name);
	}
	/**
	 * Writes the response to the socket and closes the stream.
	 * @param os stream to write to
	 * @throws IOException when writing fails
	 */
	public void write(OutputStream os) throws IOException {
		write(os, null);
	}
	
	/**
	 * Writes the response to the socket and closes the stream.
	 * @param os The outputstream to write the response to
	 * @param sessionId the sessionId to be embedded in the response
	 * @throws IOException if writing fails
	 */
	@SuppressWarnings("unchecked")
	protected void write(OutputStream os, String sessionId) 
	throws IOException {		
		PrintWriter pw = new PrintWriter(os);
		pw.write("HTTP/1.0 ");
		pw.write(code.toString());
		pw.write("\r\n");
		for (Map.Entry key : headers.entrySet()) {
			pw.write((String) key.getKey());
			pw.write(": ");
			pw.write((String) key.getValue());
			pw.write("\r\n");
		}
		if (sessionId != null) {
			pw.write("set-cookie: JSESSID=");
			pw.write(sessionId);
			pw.write("\r\n");
			
		}
		pw.write("\r\n");
		pw.flush();
		os.write(content);
		os.flush();
		os.close();
	}
}
